using System;
using System.Collections.Generic;
using System.Text;
using Castle.MonoRail.Framework.Extensions.Session;
using Enyim.Caching;
using System.Collections.Specialized;
using Enyim.Caching.Memcached;
using System.Collections;
using log4net;

namespace AjileJedi.MemcachedForMonorail.CustomSessionStateExtensions
{
	/// <summary>
	/// Implements a Specialized Idictionary for use in Monorail Sessions
	/// 
	/// </summary>
	/// <remarks>The Keys collection implementation may be a bottleneck...it needs testing</remarks>
	/// <seealso cref="MemCacheSessionFactory"/>
	public class MemCachedSessionDictionary : IDictionary<string, object>
	{
		#region Fields


		private MemcachedClient _mcClient;
		private string _sessionId;
		private ILog _logger = LogManager.GetLogger(typeof(MemCachedSessionDictionary));
		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the MemCachedSession class.
		/// </summary>
		/// <param name="sessionId">The unique ID for the session being initialized from memcached </param>
		public MemCachedSessionDictionary(string sessionId)
		{
			_sessionId = sessionId;
			_mcClient = new Enyim.Caching.MemcachedClient();


		}


		#endregion
		
		#region IDictionary<string,object> Members

		public void Add(string key, object value)
		{
			this.WriteMemCachedValue(key, value);
			

		}

		public bool ContainsKey(string key)
		{
			return (this[key] != null);
			
		}

		public ICollection<string> Keys
		{
			get {

					List<string> keysList = _mcClient.Get<List<string>>(_sessionId + ":sessionkeys");
					if (keysList == null)
					{
						keysList = new List<string>();
						UpdateKeys(keysList);
					}
					return keysList;
				}
		}
		/// <summary>
		/// Replaces the List of keys stored on
		/// the memcached servers
		/// </summary>
		/// <param name="keys"></param>
		private void UpdateKeys(List<string> keys)
		{
			_mcClient.Store(StoreMode.Set,_sessionId + ":sessionkeys", keys);

		}

		public bool Remove(string key)
		{
			try
			{
				this.RemoveMemCachedValue(key);
				
			}
			catch (Exception ex)
			{
				_logger.Warn("Exception occurred whle trying to remove from memcached", ex);
				return false;
			}
			
			return true;
		}

		public bool TryGetValue(string key, out object value)
		{
			value = null;
			try
			{
				value = GetMemCachedValue(key);
				return true;
			}
			catch 
			{
				return false;
			}
			

		}

		/// <summary>
		/// This property is not currently supported
		/// </summary>
		public ICollection<object> Values
		{
			get { throw new NotSupportedException("The method or operation is not implemented."); }
		}
		/// <summary>
		/// Retrieve key values
		/// </summary>
		/// <param name="key">the of key of the value to be returned</param>
		/// <returns>Returns the value associated with the provided <paramref name="key"/>.
		/// For keys that do not exist a <see cref="null"/> value will be returned</returns>
		public object this[string key]
		{
			get
			{
				object returnValue;
				TryGetValue(key, out returnValue);
				return returnValue;
			}
			set
			{
				this.Add(key, value);
			}
		}

		#endregion

		#region ICollection<KeyValuePair<string,object>> Members

		public void Add(KeyValuePair<string, object> item)
		{
			this.Add(item.Key, item.Value);
		}

		public void Clear()
		{
			IList<string> keys = (IList<string>)this.Keys;
			foreach (string key  in keys)
			{
				this.Remove(key);
			}
			this.UpdateKeys(null);

		}

		public bool Contains(KeyValuePair<string, object> item)
		{
			return (this.ContainsKey(item.Key) && this[item.Key].Equals(item.Value));
		}

		public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
		{
			throw new NotSupportedException("The method or operation is not implemented.");
		}

		public int Count
		{
			get { return this.Keys.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove(KeyValuePair<string, object> item)
		{
			try
			{
				this.Remove(item.Key);
				return true;
			}
			catch 
			{
				return false;
			}
			

		}

		#endregion

		#region IEnumerable<KeyValuePair<string,object>> Members

		public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
		{
			throw new Exception("The method or operation is not implemented.");
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			throw new Exception("The method or operation is not implemented.");
		}

		#endregion

		#region Private Members

		private string GetMemCachedKey(string sessionKey)
		{
			return _sessionId + ":sessionkey:" + sessionKey;
		}

		private object GetMemCachedValue(string key)
		{
			return _mcClient.Get(GetMemCachedKey(key));

		}

		private void WriteMemCachedValue(string key, object value)
		{
			_mcClient.Store(StoreMode.Set, GetMemCachedKey(key), value);

			ICollection<string> keys = this.Keys;

			if (!keys.Contains(key))
			{
				// Add the new key to the key list
				keys.Add(key);
				this.UpdateKeys((List<string>)keys);
			}



		}

		private void RemoveMemCachedValue(string key)
		{
			_mcClient.Remove(GetMemCachedKey(key));

			ICollection<string> keys = this.Keys;

			if (keys.Contains(key))
			{
				// Add the new key to the key list
				keys.Remove(key);
				this.UpdateKeys((List<string>)keys);
			}
		}
		#endregion
				
	}
}
